/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.alien.admin.modules.publisher.service.impl;

import com.alien.admin.modules.message.service.MessageNotifier;
import com.alien.admin.modules.publisher.service.PublishSheetFlowService;
import com.alien.admin.modules.publisher.service.dto.PublishSheetFlowDto;
import com.alien.admin.modules.publisher.service.mapstruct.PublishSheetFlowMapper;
import com.alien.admin.modules.publisher.domain.PublishSheetApprovalRecord;
import com.alien.admin.modules.publisher.domain.PublishSheetFlow;
import com.alien.admin.utils.ValidationUtil;
import com.alien.admin.utils.FileUtil;
import com.alien.admin.utils.enums.FlowHandleEnum;
import com.alien.admin.utils.enums.FlowNodeIdEnum;
import com.alien.admin.utils.enums.FlowStatusEnum;
import com.alien.admin.modules.publisher.repository.PublishSheetApprovalRecordRepository;
import com.alien.admin.modules.publisher.service.PublishSheetApprovalRecordService;
import com.alien.admin.modules.publisher.service.dto.PublishSheetApprovalRecordDto;
import com.alien.admin.modules.publisher.service.dto.PublishSheetApprovalRecordQueryCriteria;
import com.alien.admin.modules.publisher.service.mapstruct.PublishSheetApprovalRecordMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.alien.admin.utils.PageUtil;
import com.alien.admin.utils.QueryHelp;

import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;

/**
 * @author zengzhifang
 * @website https://el-admin.vip
 * @description 服务实现
 * @date 2021-10-25
 **/
@Service
@Slf4j
public class PublishSheetApprovalRecordServiceImpl implements PublishSheetApprovalRecordService {

    @Autowired
    private PublishSheetApprovalRecordRepository publishSheetApprovalRecordRepository;
    @Autowired
    private PublishSheetApprovalRecordMapper publishSheetApprovalRecordMapper;
    @Autowired
    private PublishSheetFlowService publishSheetFlowService;
    @Autowired
    private PublishSheetFlowMapper publishSheetFlowMapper;
    @Autowired
    private MessageNotifier messageNotifier;

    @Override
    public Map<String, Object> queryAll(PublishSheetApprovalRecordQueryCriteria criteria, Pageable pageable) {
        Page<PublishSheetApprovalRecord> page = publishSheetApprovalRecordRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(publishSheetApprovalRecordMapper::toDto));
    }

    @Override
    public List<PublishSheetApprovalRecordDto> queryAll(PublishSheetApprovalRecordQueryCriteria criteria) {
        return publishSheetApprovalRecordMapper.toDto(publishSheetApprovalRecordRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public PublishSheetApprovalRecordDto findById(Long id) {
        PublishSheetApprovalRecord publishSheetApprovalRecord = publishSheetApprovalRecordRepository.findById(id).orElseGet(PublishSheetApprovalRecord::new);
        ValidationUtil.isNull(publishSheetApprovalRecord.getId(), "PublishSheetApprovalRecord", "id", id);
        return publishSheetApprovalRecordMapper.toDto(publishSheetApprovalRecord);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PublishSheetApprovalRecordDto create(PublishSheetApprovalRecord resources) {
        processPublishSheet(resources);
        return publishSheetApprovalRecordMapper.toDto(publishSheetApprovalRecordRepository.save(resources));
    }

    /**
     * 创建一个审批记录的时候，一定会导致申请单的状态发生变化！
     *
     * @param resources
     */
    public void processPublishSheet(PublishSheetApprovalRecord resources) {

        // currentFlowNodeId其实在申请单据那边也有记录
        Long publishSheetId = resources.getPublishSheetFlowId();
        PublishSheetFlowDto publishSheetDto = publishSheetFlowService.findById(publishSheetId);
        PublishSheetFlow publishSheetPo = publishSheetFlowMapper.toEntity(publishSheetDto);

        Integer handle = resources.getHandle();

        // 通过： 走到下一个节点
        if (handle.equals(FlowHandleEnum.Pass.getId()) || handle.equals(FlowHandleEnum.Begin.getId())) {
            // 如果是结束的转态，那么不会被处理，所以这里可以大胆前进
            gotoNextFlowNode(publishSheetPo);
            // 处理是否需要测试介入逻辑
            skipTesterBusiness(publishSheetPo);
            publishSheetFlowService.update(publishSheetPo);
        }

        // 驳回：返回到起点
        if (handle.equals(FlowHandleEnum.Reject.getId())) {
            publishSheetPo.setFlowNodeId(FlowNodeIdEnum.DraftByDeveloper.getId());
            publishSheetPo.setProcesserId(publishSheetPo.getApplierId());
            publishSheetPo.setFlowStatus(FlowStatusEnum.Reject.getId());
            publishSheetFlowService.update(publishSheetPo);
        }

        // 如果是转发，那么更新处理者即可，其他字段不需要改变
        if (handle.equals(FlowHandleEnum.Forward.getId())) {
            // 一般不会出现转发，但是给谁却没指定的情况。
            if (resources.getFlowNodeId() == null) {
                log.error("forwardTo is null");
            } else {
                publishSheetPo.setProcesserId(resources.getForwardTo());
                publishSheetFlowService.update(publishSheetPo);
            }
        }

        // 废弃
        if (handle.equals(FlowHandleEnum.DISCARD.getId())) {
            publishSheetPo.setFlowNodeId(FlowNodeIdEnum.Finish.getId());
            publishSheetPo.setProcesserId(null);
            publishSheetPo.setFlowStatus(FlowStatusEnum.Terminate.getId());
            publishSheetFlowService.update(publishSheetPo);
        }

        notifyMessage(publishSheetPo, handle);
    }

    void notifyMessage(PublishSheetFlow publishSheetFlow, Integer handle) {
        Long applierId = publishSheetFlow.getApplierId();
        boolean pass = handle.equals(FlowHandleEnum.Pass.getId());
        if (pass || handle.equals(FlowHandleEnum.Begin.getId())) {
            if (pass) {
                // 通知发起者，审核通过
                String message = String.format("您发起的申请单[%s]已被审核通过", publishSheetFlow.getTitle());
                messageNotifier.sendInfoMessage(applierId, "审核通过提醒", message, makeRouter(publishSheetFlow));
            }

            // 通知当前处理人，待审核
            Long processerId = publishSheetFlow.getProcesserId();
            if (processerId != null) {
                String message = String.format("您有一项申请单[%s]等待审核", publishSheetFlow.getTitle());
                messageNotifier.sendInfoMessage(processerId, "待审核提醒", message, makeRouter(publishSheetFlow));
            }
        } else if (handle.equals(FlowHandleEnum.Reject.getId())) {
            // 通知发起者，审核被驳回
            String message = String.format("您发起的申请单[%s]被驳回", publishSheetFlow.getTitle());
            messageNotifier.sendInfoMessage(applierId, "审核驳回提醒", message, makeRouter(publishSheetFlow));
        }
    }

    String makeRouter(PublishSheetFlow publishSheetFlow) {
        return String.format("/publish/apply?publishId=%d", publishSheetFlow.getId());
    }

    /**
     * 让当前申请单据流转到下一个节点，同时更新处理人。
     *
     * @param po
     */
    public void gotoNextFlowNode(PublishSheetFlow po) {

        // 更新节点
        po.setFlowNodeId(po.getFlowNodeId() + 1);

        // 更新处理人
        Long processorId = PublishSheetFlowUtils.getProcessorIdByPublishSheetFlow(po);
        po.setProcesserId(processorId);

        // 更新状态
        if (po.getFlowNodeId().equals(FlowNodeIdEnum.Finish.getId())) {
            po.setFlowStatus(FlowStatusEnum.Finish.getId());
        } else {
            po.setFlowStatus(FlowStatusEnum.Process.getId());
        }
    }

    /**
     * 处理是否需要绕过测试。
     * 如果不需要测试，那么往前走一步！
     *
     * @param publishSheetPo
     */
    public void skipTesterBusiness(PublishSheetFlow publishSheetPo) {
        // 如果不需要测试介入，那么再往前走一步！
        if (publishSheetPo.getHasTester().equals(0) &&
                PublishSheetFlowUtils.isTesterNode(publishSheetPo.getFlowNodeId())) {
            gotoNextFlowNode(publishSheetPo);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(PublishSheetApprovalRecord resources) {
        PublishSheetApprovalRecord publishSheetApprovalRecord = publishSheetApprovalRecordRepository.findById(resources.getId()).orElseGet(PublishSheetApprovalRecord::new);
        ValidationUtil.isNull(publishSheetApprovalRecord.getId(), "PublishSheetApprovalRecord", "id", resources.getId());
        publishSheetApprovalRecord.copy(resources);
        publishSheetApprovalRecordRepository.save(publishSheetApprovalRecord);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            publishSheetApprovalRecordRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<PublishSheetApprovalRecordDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (PublishSheetApprovalRecordDto publishSheetApprovalRecord : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("发布申请单id", publishSheetApprovalRecord.getPublishSheetFlowId());
            map.put("创建时间", publishSheetApprovalRecord.getCreateTime());
            map.put("节点id", publishSheetApprovalRecord.getFlowNodeId());
            map.put("操作人id", publishSheetApprovalRecord.getOperatorId());
            map.put("操作人姓名", publishSheetApprovalRecord.getOperator());
            map.put("操作（0-提交文档 1-通过 2-驳回 3-结束流程）", publishSheetApprovalRecord.getHandle());
            map.put("处理意见,文本", publishSheetApprovalRecord.getOpinions());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
